package com.omt.tarjimdinek.web.home;

import java.text.MessageFormat;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.util.StringUtils;

import com.omt.tarjimdinek.domain.user.Service.UtilisateurRepository;
import com.omt.tarjimdinek.domain.user.Service.ValidationMembreRepository;
import com.omt.tarjimdinek.domain.user.entity.Utilisateur;
import com.omt.tarjimdinek.domain.user.entity.ValidationMembre;
import com.omt.tarjimdinek.util.Constant;
import com.omt.tarjimdinek.util.MailHelper;
import com.omt.tarjimdinek.util.StringHelper;
import com.omt.tarjimdinek.util.UserContextUtil;

/**
 * Register a new member
 * @author chabane
 *
 */
@Named("subscription")
@Scope("session")
public class SubscriptionController {

	private ResourceBundle message = ResourceBundle.getBundle("localization.messages", UserContextUtil.getLocaleInstance());
	private SubscriptionCriteria criteria = new SubscriptionCriteria();
    private static final Logger log = Logger.getLogger(SubscriptionController.class);
    
    /** used to define new password **/
    private Utilisateur currentAccount;
    private ValidationMembre currentMemberValidation;
    private boolean canChangeHisPassword = false;
    
    @Inject
    private UtilisateurRepository accountRepository;
    
    @Inject
    private ValidationMembreRepository validationMembreRepository;
    
    /** used to subscribe new member */
    private boolean step1 = true;
    private boolean step2 = false;
    private boolean step3 = false;
    
    /** email to get my password */
    private String emailForgotPassword;
    
	/**
	 * Validate subscription form
	 */
	public String validate() {
		
		if(log.isDebugEnabled()) {
			log.debug("Début méthode validate");
		}
		
		// Check if informations are not empty
		if(criteria.isControleFirstFormOK()) {
			// Encrypt password
			try {
				criteria.setMotDePasse1(StringHelper.encrypt(criteria.getMotDePasse1()));
			} catch (Exception e) {
				addMessage(FacesMessage.SEVERITY_ERROR, "main.subscription.title.error", "main.subscription.body.error");
				log.error("Error --> "+e.getStackTrace());
				return null;
			}
			// find user with same email
			Utilisateur lUtilisateur = accountRepository.getUtilisateurByUserName(criteria.getEmail1());
			
			// It's a new user - redirect to step 2
			if(lUtilisateur == null) {
				step1 = !step1;
				step2 = !step2;
				criteria.setEmail2(null);
				criteria.setMotDePasse2(null);
			} // User exist
			else {
				addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.emailexist.error");
			}
		} else {
			addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.champinvalide.error");
		}
		
		if(log.isDebugEnabled()) {
			log.debug("Fin méthode validate");
		}
		return null;
	}
	
	/**
	 * Final validation of subscription form
	 */
	public String inscription() {
		
		if(log.isDebugEnabled()) {
			log.debug("Début méthode inscription");
		}
		
		// Check if informations are not empty
		if(criteria.isControleSecondFormOK()) {
			// Check if the two email are the same
			if(criteria.getEmail1().equals(criteria.getEmail2())) {
				
				// Encrypt password
				try {
					criteria.setMotDePasse2(StringHelper.encrypt(criteria.getMotDePasse2()));
				} catch (Exception e) {
					addMessage(FacesMessage.SEVERITY_ERROR, "main.subscription.title.error", "main.subscription.body.error");
					log.error("Error --> "+e.getStackTrace());
					return null;
				}
				// Check if the two passwords are the same
				if(criteria.getMotDePasse1().equals(criteria.getMotDePasse2())){
					// Create a user
					Utilisateur lUtilisateur = accountRepository.createNewUtilisateur(criteria);
					if(lUtilisateur != null) {
						ValidationMembre lValidationMembre = validationMembreRepository.createValidationMembre(lUtilisateur.getMembre().getEmail(), ValidationMembre.VALIDER_INSCRIPTION);
						step2 = !step2;
						step3 = !step3;
						envoyerEmail(lValidationMembre, "main.subscription.mail.confirmation.content", "main.subscription.mail.confirmation.subject");
					} else {
						addMessage(FacesMessage.SEVERITY_ERROR, "main.subscription.title.error", "main.subscription.body.error");
						return null;
					}
				} else {
					addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.mdpnonsimilaire.error");
					return null;
				}
			} else {
				addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.emailnonsimilaire.error");
				return null;
			}
		} else {
			addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.champinvalide.error");
		}
		if(log.isDebugEnabled()) {
			log.debug("Fin méthode inscription");
		}
		return null;
	}
	
	/**
	 * Back to step 1
	 */
	public void retourStep1(){
		
		if(log.isDebugEnabled()) {
			log.debug("Début méthode retourStep1");
		}
		
		step1 = !step1;
		step2 = !step2;
		
		if(log.isDebugEnabled()) {
			log.debug("Fin méthode retourStep1");
		}
	}
	
	/**
	 * Back to home
	 */
	public void retour() {
		
		if(log.isDebugEnabled()) {
			log.debug("Début méthode retour");
		}
		
		step1 = !step1;
		step3 = !step3;
		criteria = new SubscriptionCriteria();
		
		if(log.isDebugEnabled()) {
			log.debug("Fin méthode retour");
		}
	}
	
	/**
	 * Sent an email to member
	 * @param lUtilisateur
	 */
	private void envoyerEmail(ValidationMembre aValidationMembre, String aContent, String aSubject) {
		
		StringBuffer lText = new StringBuffer();
		lText.append(MessageFormat.format(message.getString(aContent), aValidationMembre.getUuid(), Constant.HOST, aValidationMembre.getEmail()));
		
		StringBuffer lSubject = new StringBuffer();
		lSubject.append(message.getString(aSubject));
		
		MailHelper.envoyerEmail(aValidationMembre.getEmail(), null, Constant.NO_REPLY_EMAIL, lSubject.toString(), lText.toString(), null, getClass());
	}

	/**
	 * Add a message to faces context
	 * @param aTitle
	 * @param aContent
	 */
	public void addMessage(Severity aSeverity, String aTitle, String aContent) {
		
		if(aSeverity == null || !StringUtils.hasLength(aContent)) {
			return;
		}
		String lTitle = aTitle;
		Severity lSeverity = aSeverity;

		if(aSeverity.equals(FacesMessage.SEVERITY_ERROR) && !StringUtils.hasLength(aTitle)) {
			lTitle = "main.subscription.defaultTitle.error";
		}
		
		String lContent = message.getString(aContent);

		lContent = StringHelper.encodeToUTF8(lContent);
		if(StringUtils.hasLength(aTitle)) {
			lTitle = message.getString(lTitle);
			lTitle = StringHelper.encodeToUTF8(lTitle);
		}
			
		FacesContext.getCurrentInstance().addMessage(null, 
				new FacesMessage(lSeverity, lTitle, lContent)); 
	}

	/**
	 * Get my password
	 */
	public String forgotMyPassword(){
		
		if(StringHelper.validate(emailForgotPassword, Constant.REGEX_EMAIL)) {
			ValidationMembre lRecupererMotDePasse = validationMembreRepository.createValidationMembre(emailForgotPassword, ValidationMembre.RECUPERER_MOT_DE_PASSE);
			envoyerEmail(lRecupererMotDePasse, "main.forgotPassword.mail.confirmation.content", "main.forgotPassword.mail.confirmation.subject");
					
			addMessage(FacesMessage.SEVERITY_INFO, "", "main.forgotPassword.mail.confirmation"); 
		} else {
			addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.champinvalide.error");
			return null;
		}
		return "/home.faces?faces-redirect=true";
	}
	
	/**
	 * Validate an account
	 * @return Message to user
	 */
	public String accountValidate(){
		String lRetourMessage = "";
		
		FacesContext facesContext = FacesContext.getCurrentInstance();
		// get parameters
		String lUserId = (String) facesContext.getExternalContext().getRequestParameterMap().get("user");
		String lEmail = (String) facesContext.getExternalContext().getRequestParameterMap().get("email");
		// check if parameters are corrects
		if(StringHelper.validate(lEmail, Constant.REGEX_EMAIL) &&
				StringHelper.validate(lUserId, Constant.REGEX_UUID)) {
			// get validationMembre object
			ValidationMembre lValidationMembre = validationMembreRepository.findValidationMembre(lUserId, lEmail);
			if(lValidationMembre != null){
				// account has been validated 
				if(lValidationMembre.getValidated()){
					lRetourMessage = message.getString("main.subscription.mail.confirmation.has.been.activated");
				} else {
					// Activate account
					Utilisateur lAccount = accountRepository.getUtilisateurByUserName(lEmail);
					
					// validate a member
					lValidationMembre.setValidated(Boolean.TRUE);
					validationMembreRepository.merge(lValidationMembre);
					
					// Activate account
					lAccount.setEnabled(Boolean.TRUE);
					accountRepository.merge(lAccount);
					
					lRetourMessage = message.getString("main.subscription.mail.confirmation.validate");
				}
			} else {
				lRetourMessage = message.getString("main.subscription.mail.confirmation.unknown.link");
			}
		} else {
			lRetourMessage = message.getString("main.subscription.mail.confirmation.unknown.link");
		}
		return StringHelper.encodeToUTF8(lRetourMessage);
	}
	
	/**
	 * go to change the password of member
	 * @return message
	 */
	public String getPassword() {
		
		String lRetourMessage = message.getString("main.getPassword.description.unknown.link");
		
		// check if user repeat submit a request of password change
		if(currentAccount != null || currentMemberValidation != null || canChangeHisPassword) {
			currentAccount = null;
			currentMemberValidation = null;
			canChangeHisPassword = false;
			criteria = new SubscriptionCriteria();
		}
		
		canChangeHisPassword = false;
		
		FacesContext facesContext = FacesContext.getCurrentInstance();
		// get parameters
		String lUserId = (String) facesContext.getExternalContext().getRequestParameterMap().get("user");
		String lEmail = (String) facesContext.getExternalContext().getRequestParameterMap().get("email");
		// check if parameters are corrects
		if(StringHelper.validate(lEmail, Constant.REGEX_EMAIL) &&
				StringHelper.validate(lUserId, Constant.REGEX_UUID)) {
			// get validationMembre object
			ValidationMembre lValidationMembre = validationMembreRepository.findValidationMembre(lUserId, lEmail);
			if(lValidationMembre != null){
				// account has been validated 
				if(lValidationMembre.getValidated()){
					lRetourMessage = message.getString("main.getPassword.description.has.been.reseted");
				} else {
					// Activate account
					Utilisateur lAccount = accountRepository.getUtilisateurByUserName(lEmail);
					
					// disable account if user have forgot his password
					lAccount.setEnabled(Boolean.FALSE);
					accountRepository.merge(lAccount);
					
					// save current informations
					currentAccount = lAccount;
					currentMemberValidation = lValidationMembre;
					criteria = new SubscriptionCriteria();
					canChangeHisPassword = true;
					
					lRetourMessage = message.getString("main.getPassword.enter.new.password");
				}
			}
		}
		return StringHelper.encodeToUTF8(lRetourMessage);
	}
	
	/**
	 * go change the password
	 */
	public String doChangePassword(){
		
		String lResult = null;
		if(currentAccount != null && currentMemberValidation != null && canChangeHisPassword) {
			
			// Encrypt password
			try {
				criteria.setMotDePasse1(StringHelper.encrypt(criteria.getMotDePasse1()));
				criteria.setMotDePasse2(StringHelper.encrypt(criteria.getMotDePasse2()));
			} catch (Exception e) {
				addMessage(FacesMessage.SEVERITY_ERROR, "main.subscription.title.error", "main.subscription.body.error");
				log.error("Error --> "+e.getStackTrace());
				return lResult;
			}
			
			// Check if the two passwords are the same
			if(criteria.getMotDePasse1().equals(criteria.getMotDePasse2())){
				
				// set password of member, and reactivate the account
				currentAccount.setPassword(criteria.getMotDePasse2());
				currentAccount.setEnabled(Boolean.TRUE);
				accountRepository.merge(currentAccount);
				
				// set information of changing password
				currentMemberValidation.setValidated(Boolean.TRUE);
				validationMembreRepository.merge(currentMemberValidation);
				
				addMessage(FacesMessage.SEVERITY_INFO, "", "mail.getPassword.description.update.password.done"); 
				
				currentAccount = null;
				currentMemberValidation = null;
				canChangeHisPassword = false;
				criteria = new SubscriptionCriteria();
				
				lResult = "/home.faces?faces-redirect=true";
			} else {
				addMessage(FacesMessage.SEVERITY_ERROR, null, "main.subscription.valider.mdpnonsimilaire.error");
				return lResult;
			}
		}
		return lResult;
	}
	
	/**
	 * @return the message
	 */
	public ResourceBundle getMessage() {
		return message;
	}

	/**
	 * @param message the message to set
	 */
	public void setMessage(ResourceBundle message) {
		this.message = message;
	}

	/**
	 * @return the criteria
	 */
	public SubscriptionCriteria getCriteria() {
		return criteria;
	}

	/**
	 * @param criteria the criteria to set
	 */
	public void setCriteria(SubscriptionCriteria criteria) {
		this.criteria = criteria;
	}

	/**
	 * @return the accountRepository
	 */
	public UtilisateurRepository getAccountRepository() {
		return accountRepository;
	}

	/**
	 * @param accountRepository the accountRepository to set
	 */
	public void setAccountRepository(UtilisateurRepository accountRepository) {
		this.accountRepository = accountRepository;
	}

	/**
	 * @return the step1
	 */
	public boolean isStep1() {
		return step1;
	}

	/**
	 * @param step1 the step1 to set
	 */
	public void setStep1(boolean step1) {
		this.step1 = step1;
	}

	/**
	 * @return the step2
	 */
	public boolean isStep2() {
		return step2;
	}

	/**
	 * @param step2 the step2 to set
	 */
	public void setStep2(boolean step2) {
		this.step2 = step2;
	}

	/**
	 * @return the step3
	 */
	public boolean isStep3() {
		return step3;
	}

	/**
	 * @param step3 the step3 to set
	 */
	public void setStep3(boolean step3) {
		this.step3 = step3;
	}

	/**
	 * @return the validationMembreRepository
	 */
	public ValidationMembreRepository getValidationMembreRepository() {
		return validationMembreRepository;
	}

	/**
	 * @param validationMembreRepository the validationMembreRepository to set
	 */
	public void setValidationMembreRepository(
			ValidationMembreRepository validationMembreRepository) {
		this.validationMembreRepository = validationMembreRepository;
	}

	/**
	 * @return the emailForgotPassword
	 */
	public String getEmailForgotPassword() {
		return emailForgotPassword;
	}

	/**
	 * @param emailForgotPassword the emailForgotPassword to set
	 */
	public void setEmailForgotPassword(String emailForgotPassword) {
		this.emailForgotPassword = emailForgotPassword;
	}

	public boolean isCanChangeHisPassword() {
		return canChangeHisPassword;
	}

	public void setCanChangeHisPassword(boolean canChangeHisPassword) {
		this.canChangeHisPassword = canChangeHisPassword;
	}
}
